package com.ogms.dge.container.modules.data.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.ValidationMessage;
import com.ogms.dge.container.common.exception.BusinessException;
import com.ogms.dge.container.common.utils.*;
import com.ogms.dge.container.modules.common.entity.DataSourceEntity;
import com.ogms.dge.container.modules.common.entity.UploadRecordEntity;
import com.ogms.dge.container.modules.common.enums.UploadRecordStatusEnum;
import com.ogms.dge.container.modules.common.enums.UploadRecordTypeEnum;
import com.ogms.dge.container.modules.common.service.DataSourceService;
import com.ogms.dge.container.modules.common.service.UploadRecordService;
import com.ogms.dge.container.modules.data.converter.ConfigConverter;
import com.ogms.dge.container.modules.data.converter.SourceConverter;
import com.ogms.dge.container.modules.data.dao.SourceDao;
import com.ogms.dge.container.modules.data.dto.InstanceDto;
import com.ogms.dge.container.modules.data.dto.SourceDto;
import com.ogms.dge.container.modules.data.entity.ServiceEntity;
import com.ogms.dge.container.modules.data.entity.SourceEntity;
import com.ogms.dge.container.modules.data.service.ServiceService;
import com.ogms.dge.container.modules.data.service.SourceService;
import com.ogms.dge.container.modules.fs.entity.dto.UploadResultDto;
import com.ogms.dge.container.modules.fs.entity.enums.FileTypeEnums;
import com.ogms.dge.container.modules.fs.entity.enums.UploadStatusEnums;
import com.ogms.dge.container.modules.fs.service.impl.FileInfoServiceImpl;
import com.ogms.dge.container.modules.sys.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.*;

import static com.ogms.dge.container.common.utils.FileUtils.deleteAllExcept;
import static com.ogms.dge.container.common.utils.FileUtils.getFileNameWithoutSuffix;


@Service("sourceService")
public class SourceServiceImpl extends ServiceImpl<SourceDao, SourceEntity> implements SourceService {

    @Resource
    private SourceConverter sourceConverter;

    @Autowired
    @Lazy
    private SysUserService sysUserService;

    @Autowired
    @Lazy
    private DataSourceService dataSourceService;

    @Resource
    private ConfigConverter configConverter;

    @Value("${container.data.dsd}")
    private String data_dsd;

    @Value("${container.data.td}")
    private String data_td;

    private static ObjectMapper objectMapper = new ObjectMapper();
    @Autowired
    private UploadRecordService uploadRecordService;
    @Autowired
    @Lazy
    private ServiceService serviceService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SourceEntity> page = this.page(
                new Query<SourceEntity>().getPage(params),
                new QueryWrapper<SourceEntity>()
        );
        PageUtils pageUtils = new PageUtils(page);
        pageUtils.setList(sourceConverter.poList2VoList(page.getRecords(), sysUserService));

        return pageUtils;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> uploadSource(Long userId, MultipartFile file, String fileName, Integer chunkIndex, Integer chunks) {
        String uuid = UUID.randomUUID().toString();
        File tempFileFolder = null;
        Long recordId = 0L; // 上传记录的id
        boolean uploadSuccess = true;
        try {
            UploadResultDto resultDto = new UploadResultDto();
            // resultDto.setFileId(fileId);
            Date curDate = new Date();

            // 暂存在临时目录
            String tempFolderName = data_td;
            String currentUserFolderName = getFileNameWithoutSuffix(fileName);
            // 创建临时目录
            tempFileFolder = new File(tempFolderName + currentUserFolderName);
            if (!tempFileFolder.exists()) {
                tempFileFolder.mkdirs();
            }
            String tmpFolderPath = tempFileFolder.getPath();
            File newFile = new File(tempFileFolder.getPath() + "/" + chunkIndex);
            file.transferTo(newFile);
            // 不是最后一个分片，直接返回
            if (chunkIndex < chunks - 1) {
                resultDto.setStatus(UploadStatusEnums.UPLOADING.getCode());
                Map<String, Object> result = new HashMap<>();
                result.put("data", resultDto);
                return result;
            }
            // 最后一个分片上传完成，记录数据库，异步合并分片
            String fileSuffix = StringTools.getFileSuffix(fileName);
            // 真实文件名 3178033358LJOgdfrYOj.ico
            String realFileName = currentUserFolderName + fileSuffix;
            FileTypeEnums fileTypeEnum = FileTypeEnums.getFileTypeBySuffix(fileSuffix);

            // Long totalSize = redisComponent.getFileTempSize(webUserDto.getUserId(),
            // fileId);
            // updateUserSpace(webUserDto, totalSize);
            resultDto.setStatus(UploadStatusEnums.UPLOAD_FINISH.getCode());
            UploadRecordEntity record = uploadRecordService.saveOrUpdate(null, userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.UPLOAD_TRANSFER, uuid, "");
            recordId = record.getId();
            // 事务提交后调用异步方法
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    System.out.println("上传结束，开始转码...");

                    File targetFolder = new File(data_dsd + uuid);
                    if (!targetFolder.exists()) {
                        targetFolder.mkdirs();
                    }
                    String realFileName = currentUserFolderName + fileSuffix;
                    String targetFilePath = targetFolder.getPath() + "/" + realFileName;
                    FileInfoServiceImpl.union(tmpFolderPath, targetFilePath, fileName, true);
                    try {
                        List<File> exceptFiles = new ArrayList<>();
                        exceptFiles.add(new File(targetFilePath));
                        deleteAllExcept(targetFolder, exceptFiles);
                        FileUtils.unzip(targetFilePath, targetFolder); // 解压
                        new File(targetFilePath).delete(); // 删除原压缩包
                        uploadRecordService.saveOrUpdate(record.getId(), userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.FIRST_CONFIG, uuid, "上传成功");
                    } catch (IOException e) {
                        uploadRecordService.saveOrUpdate(record.getId(), userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.UPLOAD_FAIL, uuid, "Internal Server Error");
                        FileUtils.deleteDirectory(targetFolder.getAbsolutePath()); // 删除创建的目录和里面解压的文件
                        e.printStackTrace();
                    }
                    // 解压
                }
            });
            Map<String, Object> result = new HashMap<>();
            result.put("data", resultDto);
            result.put("record", record);
            return result;
        } catch (Exception e) {
            uploadSuccess = false;
            uploadRecordService.saveOrUpdate(recordId, userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.UPLOAD_FAIL, uuid, "Upload Failed");
            throw new BusinessException("文件上传失败");
        } finally {
            // 如果上传失败，清除临时目录
            if (tempFileFolder != null && !uploadSuccess) {
                try {
                    FileUtils.deleteDirectory(tempFileFolder.getPath());
                } catch (Exception e) {
                    log.error("删除临时目录失败");
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long[] ids) {
        for (Long id : ids) {
            serviceService.delBySrcId(id);
            SourceEntity source = getById(id);
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    FileUtils.deleteDirectory(new File(data_dsd + source.getUuid()).getAbsolutePath());
                }
            });
        }
        removeByIds(Arrays.asList(ids));
    }

    @Override
    public void extract(UploadRecordEntity record, Long userId, Date curDate, String uuid, String fileName, File targetFolder) throws JsonProcessingException {
        Map<String, Object> resultMap = new HashMap<>();
        if (!validate(resultMap).isEmpty()) {
            uploadRecordService.saveOrUpdate(record.getId(), userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.UPLOAD_FAIL, uuid, "Validation failed: the config.json file is invalid. Please validate it first.");
            FileUtils.deleteDirectory(targetFolder.getAbsolutePath()); // 删除创建的目录和里面解压的文件
            return;
        }
        // 解析 JSON
        // 解析 Source
        SourceDto sourceDto = configConverter.map2SourceDto(resultMap);
        SourceEntity source = configConverter.sourceDto2Po(sourceDto);
        source.setUuid(uuid);
        source.setCreateTime(curDate);
        source.setCreateUserId(userId);
        save(source);

        // 解析 Instance
        List<Map<String, Object>> instances = sourceDto.getInstances();
        for (int i = 0; i < instances.size(); i++) {
            InstanceDto insDto = configConverter.map2InsDto(instances.get(i));
            ServiceEntity service = configConverter.insDto2Po(insDto, dataSourceService);
            service.setUuid(UUID.randomUUID().toString());
            service.setCreateTime(curDate);
            service.setSrcUuid(uuid);
            service.setCreateUserId(userId);
            service.setUseCount(0L);
            serviceService.save(service);
        }
        // 最后记录成功上传的记录
        uploadRecordService.saveOrUpdate(record.getId(), userId, fileName, UploadRecordTypeEnum.DATA, UploadRecordStatusEnum.SUCCESS, uuid, "解析成功");
    }

    @Override
    public List<ValidationMessage> insValidate(Map<String, Object> json) throws JsonProcessingException {
        String name = (String) json.get("Type");
        DataSourceEntity ds = dataSourceService.getOne(new QueryWrapper<DataSourceEntity>().eq("name", name));
        if (ds == null) {
            ValidationMessage msg = ValidationMessage.builder()
                    .type("error") // 或者使用其他类型，如 "warning"
                    .message("Type 属性：未找到对应 '" + name + "' 数据源") // 错误消息内容
                    .build();
            List<ValidationMessage> list = new ArrayList<>();
            list.add(msg);
            return list;
        }
        String schema = ds.getJsonSchema();
        List<ValidationMessage> list = JsonValidator.validateJson(objectMapper.writeValueAsString(json), schema);
        return list;
    }

    @Override
    public List<ValidationMessage> validate(Map<String, Object> json) throws JsonProcessingException {
        String schema = "{\n" +
                "  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n" +
                "  \"type\": \"object\",\n" +
                "  \"properties\": {\n" +
                "    \"Name\": {\n" +
                "      \"type\": \"string\"\n" +
                "    },\n" +
                "    \"Description\": {\n" +
                "      \"type\": \"string\"\n" +
                "    },\n" +
                "    \"Tags\": {\n" +
                "      \"type\": \"array\",\n" +
                "      \"items\": {\n" +
                "        \"type\": \"string\"\n" +
                "      }\n" +
                "    },\n" +
                /*"    \"Category\": {\n" +
                "      \"type\": \"string\"\n" +
                "    },\n" +
                "    \"Copyright\": {\n" +
                "      \"type\": \"string\"\n" +
                "    },\n" +*/
                "    \"Instances\": {\n" +
                "      \"type\": \"array\",\n" +
                "      \"items\": {\n" +
                "        \"type\": \"object\",\n" +
                "        \"properties\": {\n" +
                "          \"Name\": {\n" +
                "            \"type\": \"string\"\n" +
                "          },\n" +
                "          \"Description\": {\n" +
                "            \"type\": \"string\"\n" +
                "          },\n" +
                "          \"Type\": {\n" +
                "            \"type\": \"string\"\n" +
                "          }\n" +
                "        },\n" +
                "        \"patternProperties\": {\n" +
                "          \"^(RDB|NoSQL|MapService|GeoDatabase|DBFile|OSS|VectorFile|RasterFile|OtherFile)$\": {\n" +
                "            \"type\": \"object\"\n" +
                "          }\n" +
                "        },\n" +
                "        \"minProperties\": 4,\n" +
                "        \"maxProperties\": 4,\n" +
                "        \"required\": [\"Name\", \"Description\", \"Type\"]\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"required\": [\"Name\", \"Description\", \"Tags\", \"Instances\"]\n" +
                "}";
        List<ValidationMessage> list = JsonValidator.validateJson(objectMapper.writeValueAsString(json), schema);
        return list;
    }

}